# app.py (unchanged, as the request is for UI redesign which is frontend-focused)
from flask import Flask, render_template, request, jsonify, flash, redirect, url_for
import psycopg2
from psycopg2 import Error
import threading
import time
import random
import uuid
from datetime import datetime
import logging

app = Flask(__name__, static_folder='static', template_folder='templates')
app.secret_key = str(uuid.uuid4())

# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

DB_PARAMS = {
    "user": "postgres",
    "password": "ffxshlsy666",
    "host": "localhost",
    "port": "5432",
    "database": "transaction_lab"
}

def get_connection():
    try:
        conn = psycopg2.connect(**DB_PARAMS)
        logger.info("Database connection established")
        return conn
    except Error as e:
        logger.error(f"Database connection failed: {e}")
        raise

def create_account(name, initial_balance=0.00):
    try:
        connection = get_connection()
        cursor = connection.cursor()
        connection.autocommit = False
        insert_query = """
        INSERT INTO accounts (name, balance) VALUES (%s, %s)
        RETURNING account_id;
        """
        cursor.execute(insert_query, (name, initial_balance))
        account_id = cursor.fetchone()[0]
        if initial_balance > 0:
            record_transaction = """
            INSERT INTO transactions (to_account, amount) VALUES (%s, %s);
            """
            cursor.execute(record_transaction, (account_id, initial_balance))
        connection.commit()
        logger.info(f"Account created: ID {account_id}, Name: {name}, Balance: {initial_balance}")
        return account_id, True, None
    except (Exception, Error) as error:
        if connection:
            connection.rollback()
        logger.error(f"Error creating account: {error}")
        return None, False, str(error)
    finally:
        if connection:
            cursor.close()
            connection.close()

def deposit(account_id, amount):
    try:
        connection = get_connection()
        cursor = connection.cursor()
        connection.autocommit = False
        update_query = "UPDATE accounts SET balance = balance + %s WHERE account_id = %s;"
        cursor.execute(update_query, (amount, account_id))
        record_transaction = """
        INSERT INTO transactions (to_account, amount) VALUES (%s, %s);
        """
        cursor.execute(record_transaction, (account_id, amount))
        connection.commit()
        logger.info(f"Deposited {amount} into account ID {account_id}")
        return True, None
    except (Exception, Error) as error:
        if connection:
            connection.rollback()
        logger.error(f"Error during deposit: {error}")
        return False, str(error)
    finally:
        if connection:
            cursor.close()
            connection.close()

def withdraw(account_id, amount):
    try:
        connection = get_connection()
        cursor = connection.cursor()
        connection.autocommit = False
        check_balance = "SELECT balance FROM accounts WHERE account_id = %s;"
        cursor.execute(check_balance, (account_id,))
        current_balance = cursor.fetchone()
        if not current_balance:
            connection.rollback()
            logger.error(f"Account not found: {account_id}")
            return False, "Account not found."
        if current_balance[0] < amount:
            connection.rollback()
            logger.error(f"Insufficient funds in account {account_id}")
            return False, "Insufficient funds."
        update_query = "UPDATE accounts SET balance = balance - %s WHERE account_id = %s;"
        cursor.execute(update_query, (amount, account_id))
        record_transaction = """
        INSERT INTO transactions (from_account, amount) VALUES (%s, %s);
        """
        cursor.execute(record_transaction, (account_id, amount))
        connection.commit()
        logger.info(f"Withdrew {amount} from account ID {account_id}")
        return True, None
    except (Exception, Error) as error:
        if connection:
            connection.rollback()
        logger.error(f"Error during withdrawal: {error}")
        return False, str(error)
    finally:
        if connection:
            cursor.close()
            connection.close()

def transfer_money(from_account_id, to_account_id, amount):
    try:
        connection = get_connection()
        cursor = connection.cursor()
        connection.autocommit = False
        check_balance = "SELECT balance FROM accounts WHERE account_id = %s;"
        cursor.execute(check_balance, (from_account_id,))
        current_balance = cursor.fetchone()
        if not current_balance:
            connection.rollback()
            logger.error(f"Source account not found: {from_account_id}")
            return False, "Source account not found."
        if current_balance[0] < amount:
            connection.rollback()
            logger.error(f"Insufficient funds in account {from_account_id}")
            return False, "Insufficient funds."
        cursor.execute(check_balance, (to_account_id,))
        if not cursor.fetchone():
            connection.rollback()
            logger.error(f"Destination account not found: {to_account_id}")
            return False, "Destination account not found."
        update_source = "UPDATE accounts SET balance = balance - %s WHERE account_id = %s;"
        cursor.execute(update_source, (amount, from_account_id))
        update_destination = "UPDATE accounts SET balance = balance + %s WHERE account_id = %s;"
        cursor.execute(update_destination, (amount, to_account_id))
        record_transaction = """
        INSERT INTO transactions (from_account, to_account, amount) VALUES (%s, %s, %s);
        """
        cursor.execute(record_transaction, (from_account_id, to_account_id, amount))
        connection.commit()
        logger.info(f"Transferred {amount} from {from_account_id} to {to_account_id}")
        return True, None
    except (Exception, Error) as error:
        if connection:
            connection.rollback()
        logger.error(f"Error during transfer: {error}")
        return False, str(error)
    finally:
        if connection:
            cursor.close()
            connection.close()

def view_transaction_history(account_id):
    try:
        connection = get_connection()
        cursor = connection.cursor()
        query = """
        SELECT transaction_id, from_account, to_account, amount, transaction_date
        FROM transactions
        WHERE from_account = %s OR to_account = %s
        ORDER BY transaction_date DESC;
        """
        cursor.execute(query, (account_id, account_id))
        transactions = cursor.fetchall()
        logger.info(f"Retrieved transaction history for account {account_id}")
        return transactions, None
    except (Exception, Error) as error:
        logger.error(f"Error viewing transaction history: {error}")
        return [], str(error)
    finally:
        if connection:
            cursor.close()
            connection.close()

def get_all_accounts():
    try:
        connection = get_connection()
        cursor = connection.cursor()
        cursor.execute("SELECT account_id, name, balance FROM accounts;")
        accounts = cursor.fetchall()
        logger.info("Retrieved all accounts")
        return accounts, None
    except (Exception, Error) as error:
        logger.error(f"Error retrieving accounts: {error}")
        return [], str(error)
    finally:
        if connection:
            cursor.close()
            connection.close()

def get_balance_history(account_id):
    try:
        connection = get_connection()
        cursor = connection.cursor()
        cursor.execute("""
            SELECT transaction_date, amount, from_account, to_account
            FROM transactions
            WHERE from_account = %s OR to_account = %s
            ORDER BY transaction_date;
        """, (account_id, account_id))
        transactions = cursor.fetchall()
        logger.info(f"Retrieved balance history for account {account_id}")
        return transactions, None
    except (Exception, Error) as error:
        logger.error(f"Error retrieving balance history: {error}")
        return [], str(error)
    finally:
        if connection:
            cursor.close()
            connection.close()

@app.route('/')
def index():
    accounts, error = get_all_accounts()
    if error:
        flash(error, 'danger')
    return render_template('index.html', accounts=accounts)

@app.route('/api/accounts', methods=['GET'])
def api_accounts():
    accounts, error = get_all_accounts()
    if error:
        return jsonify({'error': error}), 500
    return jsonify([{'id': acc[0], 'name': acc[1], 'balance': float(acc[2])} for acc in accounts])

@app.route('/api/balance_history/<int:account_id>', methods=['GET'])
def api_balance_history(account_id):
    transactions, error = get_balance_history(account_id)
    if error:
        return jsonify({'error': error}), 500
    balance = 0
    history = []
    for tx in transactions:
        amount = float(tx[1])
        if tx[2] == account_id:  # from_account
            balance -= amount
        elif tx[3] == account_id:  # to_account
            balance += amount
        history.append({'date': tx[0].strftime('%Y-%m-%d'), 'balance': balance})
    return jsonify(history)

@app.route('/create_account', methods=['POST'])
def create_account_route():
    name = request.form['name']
    initial_balance = float(request.form['initial_balance']) if request.form['initial_balance'] else 0.00
    account_id, success, error = create_account(name, initial_balance)
    if success:
        flash(f"Account created: ID {account_id}, Name: {name}, Balance: {initial_balance:.2f}", 'success')
    else:
        flash(f"Error creating account: {error}", 'danger')
    return jsonify({'message': f"Account created: ID {account_id}, Name: {name}, Balance: {initial_balance:.2f}" if success else f"Error: {error}"})

@app.route('/deposit', methods=['POST'])
def deposit_route():
    account_id = int(request.form['account_id'])
    amount = float(request.form['amount'])
    success, error = deposit(account_id, amount)
    if success:
        flash(f"Deposited {amount:.2f} into account ID {account_id}.", 'success')
    else:
        flash(f"Error during deposit: {error}", 'danger')
    return jsonify({'message': f"Deposited {amount:.2f} into account ID {account_id}." if success else f"Error: {error}"})

@app.route('/withdraw', methods=['POST'])
def withdraw_route():
    account_id = int(request.form['account_id'])
    amount = float(request.form['amount'])
    success, error = withdraw(account_id, amount)
    if success:
        flash(f"Withdrew {amount:.2f} from account ID {account_id}.", 'success')
    else:
        flash(f"Error during withdrawal: {error}", 'danger')
    return jsonify({'message': f"Withdrew {amount:.2f} from account ID {account_id}." if success else f"Error: {error}"})

@app.route('/transfer', methods=['POST'])
def transfer_route():
    from_account_id = int(request.form['from_account_id'])
    to_account_id = int(request.form['to_account_id'])
    amount = float(request.form['amount'])
    success, error = transfer_money(from_account_id, to_account_id, amount)
    if success:
        flash(f"Transferred {amount:.2f} from ID {from_account_id} to ID {to_account_id}.", 'success')
    else:
        flash(f"Error during transfer: {error}", 'danger')
    return jsonify({'message': f"Transferred {amount:.2f} from ID {from_account_id} to ID {to_account_id}." if success else f"Error: {error}"})

@app.route('/history/<int:account_id>')
def history_route(account_id):
    transactions, error = view_transaction_history(account_id)
    if error:
        flash(f"Error viewing transaction history: {error}", 'danger')
    return render_template('history.html', transactions=transactions, account_id=account_id)

def simulate_user(user_id, account_pairs, amounts, log):
    for _ in range(5):
        from_acc, to_acc = random.choice(account_pairs)
        amount = random.choice(amounts)
        success, error = transfer_money(from_acc, to_acc, amount)
        log_entry = f"User {user_id} transferred {amount:.2f} from {from_acc} to {to_acc}: {'succeeded' if success else f'failed - {error}'}"
        log.append(log_entry)
        logger.info(log_entry)
        time.sleep(random.uniform(0.1, 0.5))

@app.route('/simulate', methods=['POST'])
def simulate_route():
    accounts, error = get_all_accounts()
    if error or not accounts:
        flash("Error retrieving accounts for simulation.", 'danger')
        return jsonify({'error': 'No accounts available'}), 500
    account_ids = [acc[0] for acc in accounts]
    account_pairs = [(i, j) for i in account_ids for j in account_ids if i != j]
    amounts = [50.00, 100.00, 150.00, 200.00]
    num_users = 5
    log = []
    threads = []
    for user_id in range(1, num_users + 1):
        thread = threading.Thread(target=simulate_user, args=(user_id, account_pairs, amounts, log))
        threads.append(thread)
        thread.start()
    for thread in threads:
        thread.join()
    return jsonify({'message': 'Simulation completed', 'log': log})

if __name__ == "__main__":
    app.run(debug=True)